# The parent of all RedBook exceptions.
class RedBookException < Exception; {} end;

# A generic RedBook error.
class GenericError < RedBookException; {} end;

# A configuration error.
class ConfigError < RedBookException; {} end;

# A logger error.
class LoggerError < RedBookException; {} end;

# A parser error.
class ParserError < RedBookException; {} end;

# A renderer error.
class RendererError < RedBookException; {} end;

# An interface error.
class InterfaceError < RedBookException; {} end;

# The String class has been extended with some methods mainly for colorizing and encoding output.
class String

	# Make the receiver red.
	def red; colorize(self, "\e[1;31m"); end

	# Make the receiver dark red.
	def dark_red; colorize(self, "\e[0;31m"); end

	# Make the receiver green.
	def green; colorize(self, "\e[1;32m"); end
	
	# Make the receiver dark green.
	def dark_green; colorize(self, "\e[0;32m"); end

	# Make the receiver yellow.
	def yellow; colorize(self, "\e[1;33m"); end

	# Make the receiver dark yellow.
	def dark_yellow; colorize(self, "\e[0;33m"); end

	# Make the receiver blue.
	def blue; colorize(self, "\e[1;34m"); end

	# Make the receiver dark blue.
	def dark_blue; colorize(self, "\e[0;34m"); end

	# Make the receiver magenta.
	def magenta; colorize(self, "\e[1;35m"); end

	# Make the receiver magenta.
	def dark_magenta; colorize(self, "\e[0;35m"); end

	# Make the receiver cyan.
	def cyan; colorize(self, "\e[1;36m"); end

	# Make the receiver dark cyan.
	def dark_cyan; colorize(self, "\e[0;36m"); end

	# Uncolorize string.
	def uncolorize;	self.gsub!(/\e\[\d[;0-9]*m/, '') end

	# Colorize the receiver according the given ASCII escape character sequence.
	# Thanks to: http://kpumuk.info/ruby-on-rails/colorizing-console-ruby-script-output/
	def colorize(text, color_code) 
		"#{color_code}#{text}\e[0m"
	end

	# Encode the receiver according to RedBook log file encoding, specified in the user configuration file.
	def encode_log
		encode(RedBook::Config.instance.data[:log_charset], RedBook::Config.instance.data[:input_charset])
	end

	# Encode the receiver according to RedBook output encoding, specified in the user configuration file.
  def encode_out
		encode(RedBook::Config.instance.data[:output_charset], RedBook::Config.instance.data[:log_charset])
	end

	# Encode the receiver according to RedBook input encoding, specified in the user configuration file.
	def encode_in
		encode(RedBook::Config.instance.data[:input_charset], RedBook::Config.instance.data[:log_charset])
	end
	# Encode the receiver according to a given encoding, specified in the user configuration file.
	def encode(to, from)
		(to != from) ? Iconv.conv(to, from, self) : self
	end
	
	# Return _true_ if a file name corresponding to the receiver exists.
	def exists?
		File.exists?(self)
	end
end

# Extension to the core +Array+ class.
class Array
	
	# Merge two arrays together, removing duplicates (modifies the receiver).
	def merge!(other_array=[])
		self.concat(other_array).uniq!
	end
	
	# Merge two arrays together, removing duplicates (does not modify the receiver).
	def merge(other_array=[])
		self.concat(other_array).uniq
	end
end

=begin

	
	# XML Hash activity
	

	
  { :activity => nil,
    :_attributes => {:message => 'text'},
    :_children => [
                    {:tag => 'tag1'},
                    {:tag => 'tag2'},
                    {:end_time => Time.now+100},
                    {:duration => 24331},
                    {:start_time => Time.now},
                  ]
  }

	
=end

# Extension to the core +Hash+ class.
class Hash

	alias :to_activesupport_xml :to_xml
	
	# Convert a Hash to XML, provided that it is XML-compatible.
	#
	# Example:
	#
	# <tt>
	#	{ :activity => nil,
  #		:_attributes => {:message => 'text'},
  # 	:_children => [
  #                  {:tag => 'tag1'},
  #                  {:tag => 'tag2'},
	#                  {:start_time => Time.now},
  #                  {:end_time => Time.now+100},
	#                  {:duration => 100}
  #                	]
  # }
	# </tt>
	#  _becomes_
	# <tt>
	#		<?xml version="1.0" ?>
	#		<activity message="text">
	#			<tag>tag1</tag>
	#			<tag>tag2</tag>
	#			<start_time>Sat Feb 02 2008 - 02:58:04 PM</start_time>
	#			<end_time>Sat Feb 02 2008 - 02:59:44 PM</end_time>
	#			<duration>100</duration>
	#		</activity>
	# </tt>
	#
	def to_xml(header=true, level=0)
		raise GenericError, "Object is not a valid XHash" unless self.xhash?
		xml = ''
		xml += %{<?xml version="1.0" ?>\n} if header
		xml += %{#{'  '*level}<#{self.name.to_s}}
		self.attributes.each_pair { |i,v|	xml += %{ #{i.to_s}="#{v.to_s}"} } if self.attributes
		unless self.children.blank? then
			level+=1
			xml += ">\n"
			xml += %{#{'  '*level}#{self.value}\n} if self.value
			self.children.each do |c|
				xml += c.to_xml(false, level)
			end
			xml += %{#{'  '*level-=1}</#{self.name.to_s}>\n}
		else
			xml += self.value ? ">#{self.value.to_s}</#{self.name.to_s}>\n" : " />\n"
		end
		xml
	end
	
	# Convert an XML-compatible activity hash to a RedBook activity hash.
	def to_activity
    begin
      r = {}
      r[:message] = self.attributes[:message]
      r[:timestamp] = (self/:start_time)[0].value
			unless (self/:tag).blank?
				r[:tags] = []
				(self/:tag).each do |t|
      		r[:tags] << t.value
				end
			end
    rescue
      raise GenericError, "Object is not a valid RedBook XHash Activity"
    end
    r
	end
	
	# Convert an XML-compatible command hash to a RedBook command hash.
	def to_command
    #begin
      r = {}
      self.attributes.each_pair do |i, v|
				r[i] = v
      end
      unless (self/:tags).blank? && (self/:tagset).blank? then
				r[:tags] = []
      end
      (self/:tag).each do |t| # Parse simple list of tags
				r[:tags] << t.value
      end
      tagset_count = 0 unless (self/:tagset).blank?
      (self/:tagset).each do |tag_collections| # Parse tagsets
				tagset = {:positive => [], :negative => []}
				tag_collections.children.each do |tags|
					tags.children.each { |t| tagset[tags[:_attributes][:type].to_sym] << t }
				end
				r[:tags] << tagset				
      end
    #rescue
    #  raise GenericError, "Object is not a valid RedBook XHash Command"
    #end
    r
	end
	
	# Convert a RedBook activity hash to an XML-compatible activity hash.
	def to_xhash_activity
    begin
      r = {}
      r[:_attributes] = {}
      r[:activity] = nil
      r[:_attributes][:message] = self[:message]
      self[:tags].each { |t| r << {:tag => t} } if self[:tags]
      r << {:start_time => self[:timestamp]}
    rescue
      raise GenericError, "Object is not a valid RedBook Activity"
    end
    r
	end
	
	# Convert a RedBook command hash to an XML-compatible command hash.
	def to_xhash_command
		begin
      r = {}
      r[:_attributes] = {}
      r[:command] = nil
      self.each_pair do |i, v|
				if i == :tags then
					v.each do |ts| # Parse each tagset alternatives
						if ts.is_a? Array then # Simple tag array
							tgs = {:tags => nil}
							ts.each do |tag|
								tgs << 	{:tag => tag}
							end
							r << tgs
						else # Assumed Hash like {:positive => [...], :negative => [...]}
							tagset = {:tagset => nil}
							unless ts[:positive].blank? then
								positive_tags = {:tags => nil, :_attributes => {:type => 'positive'}}
								ts[:positive].each {|t| positive_tags << {:tag => t} }
								tagset << positive_tags
							end
							unless ts[:negative].blank? then
								negative_tags = {:tags => nil, :_attributes => {:type => 'negative'}}
								ts[:negative].each {|t| negative_tags << {:tag => t} }
								tagset << negative_tags
							end
							r << tagset
						end						
					end
				else
					r.attributes[i] = v
				end
      end
    rescue
			raise GenericError, "Object is not a valid RedBook Command"
    end
    r
	end
	
	# Return all _sym_ children elements of the receiver.
	def /(sym)
    name = self.xhash?
    raise GenericError, "Object is not a valid XHash" unless name
    r = []
    self[:_children].each do |c|
      if c.name == sym
        r << c
      end
    end
    return r 
	end
	
	
	# Add a new child element to the receiver.
	def <<(hash)
    raise GenericError, "Object is not a valid XHash" unless hash.xhash?
    raise GenericError, "Receiver is not a valid XHash" unless self.xhash?
    self[:_children] = [] unless self[:_children]
    self[:_children] << hash
	end
	
	# Add a new attribute to the receiver.
	def <=(hash)
    raise GenericError, "Object is not a valid XHash" unless hash.xhash?
    raise GenericError, "Receiver is not a valid XHash" unless self.xhash?
    self[:_children] = {} unless self[:_children]
    self[:_attributes].merge! hash
	end
	
	# Return the receiver's children elements.
	def children
		self[:_children] ? self[:_children] : []
	end
	
	# Return the receiver's attributes.
	def attributes
		self[:_attributes] ? self[:_attributes] : {}
	end
	
	# Recurse the receiver through its children.
	def recurse
    self.children.each { |c| yield(c); c.recurse } if self.children
	end
	
	# Return the receiver XML entity name.
	def name
    name = self.xhash?
    return name if name
    raise GenericError, "Invalid XHash"
	end
	
	# Return the receiver XML entity value.
	def value
    name = self.xhash?
    return self[name] if name
    raise GenericError, "Invalid XHash"
	end
	
	# Return _true_ if the receiver is an XML-compatible hash.
	def xhash?
    name = nil
    attributes = nil
    children = nil
    self.each_pair do |i, v|
      case i
      when :_attributes
        return false unless v.is_a? Hash
      when :_children
        return false unless v.is_a? Array
      else
        return false if name
        if v.is_a? Array then
          return false
        else
          name = i
        end 
      end
    end
    begin
			self.recurse { |h| h.xhash? } if self.children
    rescue;
    ensure
			return name
    end
	end
	
	
end








